package com.turtle.security.security.service;

import com.turtle.security.common.utils.RedisCache;
import com.turtle.security.security.constant.SecurityConstants;
import com.turtle.security.security.dto.AuthUserDTO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * token操作类
 *
 * @author turtle
 * date: 2020-05-09
 */
@Component
@Slf4j
public class TokenOperatorService {
    protected static final long MILLIS_SECOND = 1000;
    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    @Autowired
    private RedisCache redisCache;

    /**
     * token标识
     */
    @Value("${security.header}")
    private String header;
    /**
     * 令牌秘钥
     */
    @Value("${security.secret}")
    private String secret;
    /**
     * 令牌有效期（默认30分钟）
     */
    @Value("${security.expireTime}")
    private int expireTime;

    public AuthUserDTO getAuthUserByToken(HttpServletRequest request) {
        // 获取请求头中的token
        String token = this.getToken(request);
        if (token != null) {
            // 解析token，获取用户信息
            Claims claims = this.parseToken(token);
            String userKey = this.getRedisKey((String) claims.get(SecurityConstants.USER_ID));
            // 通过userKey从redis获取用户信息
            AuthUserDTO authUser = redisCache.getCacheObject(userKey);
            if (authUser == null) {
                return null;
            }
            // 判断token是否匹配，不匹配则说明已被挤号
            if (!authUser.getUserKey().equals(claims.get(SecurityConstants.USER_KEY))) {
                return null;
            }
            return authUser;
        }
        return null;
    }

    /**
     * 生成token
     */
    public String generateToken(AuthUserDTO authUser) {
        // 生成uuid
        String uuid = UUID.randomUUID().toString();
        authUser.setUserKey(uuid);
        // 将用户信息存储到redis
        saveUserToRedis(authUser);
        // 生成token
        Map<String, Object> claims = new HashMap<>(2);
        claims.put(SecurityConstants.USER_KEY, uuid);
        claims.put(SecurityConstants.USER_ID, authUser.getUserId().toString());
        return createToken(claims);
    }

    /**
     * 生成token
     *
     * @param claims 数据
     * @return token
     */
    private String createToken(Map<String, Object> claims) {
        // 设置token过期时间
        Date expirationDate = new Date(System.currentTimeMillis() + expireTime * MILLIS_MINUTE);
        return Jwts.builder()
            .setExpiration(expirationDate)
            .setClaims(claims)
            .signWith(SignatureAlgorithm.HS512, secret).compact();
    }

    public void delAuthUser(String userId) {
        if (StringUtils.isNotEmpty(userId)) {
            // 获取redis key
            String redisKey = getRedisKey(userId);
            redisCache.deleteObject(redisKey);
        }
    }

    /**
     * 将用户信息保存到redis缓存
     *
     * @param authUser 认证用户信息
     */
    private void saveUserToRedis(AuthUserDTO authUser) {

        authUser.setLoginTime(System.currentTimeMillis());
        authUser.setExpireTime(authUser.getLoginTime() + expireTime * MILLIS_MINUTE);

        String redisKey = getRedisKey(authUser.getUserId().toString());

        redisCache.setCacheObject(redisKey, authUser, expireTime, TimeUnit.MINUTES);

    }

    /**
     * 获取用户唯一识别码
     *
     * @param userId 用户id
     * @return 用户唯一识别码
     */
    private String getRedisKey(String userId) {
        return SecurityConstants.REDIS_KEY + userId;
    }

    /**
     * 获取请求header里的token值
     *
     * @param request 请求体
     * @return token 凭证
     */
    public String getToken(HttpServletRequest request) {

        String token = request.getHeader(header);
        if (StringUtils.isNotEmpty(token) && token.startsWith(SecurityConstants.TOKEN_PREFIX)) {
            return token.replace(SecurityConstants.TOKEN_PREFIX, "");
        } else {
            return null;
        }

    }

    /**
     * 解析token
     *
     * @param token 凭证
     * @return Claims 数据
     */

    public Claims parseToken(String token) {

        return Jwts.parser()
            .setSigningKey(secret)
            .parseClaimsJws(token)
            .getBody();
    }
}
